package core

import (
	"fmt"
	partner "gitee.com/binny_w/gin-partner"
	"gitee.com/binny_w/go-util"
	util2 "gitee.com/binny_w/go-util/v2"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type JwtUserClaims struct {
	jwt.RegisteredClaims
	ActorName string `json:"actor_name"`
	ActorRole string `json:"actor_role"`
	Name      string `json:"name"`
	Role      string `json:"role"`
	IP        string `json:"ip"`
	UA        string `json:"ua"`
	ActorId   uint64 `json:"actor_id"`
	Id        uint64 `json:"id"`
}

func IsPublicApi(method, path string) bool {
	// 获取系统信息
	if method == http.MethodGet && path == "/api/system/info" {
		return true
	}
	// 获取存储文件
	if method == http.MethodGet && path == "/api/file/get" {
		return true
	}
	// 用户登录
	if method == http.MethodPost && path == "/api/user/login" {
		return true
	}
	// 获取非对称加密公钥
	if method == http.MethodGet && path == "/api/rsa/pub-key" {
		return true
	}
	// 滑动验证码相关的请求
	if strings.HasPrefix(path, "/api/slider-verify/") {
		return true
	}
	if gin.Mode() == gin.DebugMode && strings.HasPrefix(path, "/debug/") {
		return true
	}
	return false
}

func IsRoleLimitlessApi(method, path string) bool {
	// 用户个人信息
	if (method == http.MethodGet || method == http.MethodPatch) && path == "/api/my/info" {
		return true
	}
	// 用户修改密码
	if method == http.MethodPatch && path == "/api/my/password" {
		return true
	}
	// 用户上传头像
	if method == http.MethodPost && path == "/api/my/avatar" {
		return true
	}
	// 用户上传签名
	if method == http.MethodPost && path == "/api/my/signature" {
		return true
	}
	return false
}

func auth(c *gin.Context) {
	// 开放接口，直接放行
	if IsPublicApi(c.Request.Method, c.FullPath()) {
		c.Next()
		return
	}
	token, _ := url.QueryUnescape(c.GetHeader("Authorization"))
	// 请求中未携带Token
	if token == "" {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请登录#4")
		return
	}
	ret, err := jwt.ParseWithClaims(token, &JwtUserClaims{}, func(token *jwt.Token) (any, error) {
		return []byte(Config.GetString("jwt-key")), nil
	})
	// Token未能正常解码
	if err != nil {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#0")
		return
	}
	// Token校验不通过
	if !ret.Valid {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#7")
		return
	}
	claims, ok := ret.Claims.(*JwtUserClaims)
	// Token类型错误
	if !ok {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#8")
		return
	}
	// Token的IP、UA校验未通过
	if claims.IP != c.ClientIP() || claims.UA != c.Request.UserAgent() {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#1")
		return
	}
	var row *util2.MysqlRow
	row, err = UserTable.GetOne(c, claims.Id)
	// Token所指用户不存在
	if err != nil || row == nil {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#2")
		return
	}
	// Token所指用户权限有变动，或者用户被冻结
	if row.ToInt64("deleted_at") > 0 || row.ToStr("role") != claims.Role {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#3")
		return
	}
	issuedAt := claims.IssuedAt.Unix()
	// Token的签发时间，不可以早于最近登录，为了安全，也不要早于最后更新（比如：改了密码）
	if issuedAt < row.ToInt64("last_login") || issuedAt < row.ToInt64("updated_at") {
		partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#9")
		return
	}
	// 用户无权访问当前接口
	if !IsRoleLimitlessApi(c.Request.Method, c.FullPath()) {
		if ok, _ = Rbac.Enforce(fmt.Sprintf("user:%d", claims.Id), "api", c.Request.Method+":"+c.FullPath()); !ok {
			partner.WriteJsonError(c, http.StatusForbidden, "无权请求接口")
			return
		}
	}
	if claims.ActorId > 0 {
		// Actor账号不存在、或被禁用、或权限有变化
		if actor, _ := UserTable.GetOne(c, claims.ActorId); actor == nil || actor.ToInt64("deleted_at") > 0 || actor.ToStr("role") != claims.ActorRole {
			partner.WriteJsonError(c, http.StatusUnauthorized, "请重新登录#11")
			return
		}
	}
	// 全部验证通过，设置用户信息至上下文
	row.Set("actorId", claims.ActorId)
	row.Set("actorName", claims.ActorName)
	row.Set("actorRole", claims.ActorRole)
	c.Set("user", row)
	c.Next()
}

func GetOssKey(ext string) string {
	s := time.Now().In(util.TimeLocationBJ()).Format("060102/15/0405")
	return fmt.Sprintf("%s%s%s", s, util.StrRandom(4, ""), ext)
}
